/******************************************************************************
 *    Copyright 2013 IT2media - IT-Solutions for Directory Business           *
 *                                                                            *
 *  Licensed under the Apache License, Version 2.0 (the "License");           *
 *  you may not use this file except in compliance with the License.          *
 *  You may obtain a copy of the License at                                   *
 *                                                                            *
 *      http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                            *
 *  Unless required by applicable law or agreed to in writing, software       *
 *  distributed under the License is distributed on an "AS IS" BASIS,         *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 *  See the License for the specific language governing permissions and       *
 *  limitations under the License.                                            *
 ******************************************************************************/
package de.it2media.javasitemapgenerator.url;

import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.it2media.javasitemapgenerator.AbstractJavaSitemapGenerator;
import de.it2media.javasitemapgenerator.StringJavaSitemapGenerator;
import de.it2media.javasitemapgenerator.exceptions.PriorityOutOfRangeException;
import de.it2media.javasitemapgenerator.exceptions.ReachedMaxExtensionSizeException;
import de.it2media.javasitemapgenerator.header.XMLNS;
import de.it2media.javasitemapgenerator.url.extensions.JavaSitemapGeneratorURLExtension;

public class JavaSitemapGeneratorURL {

  /**
   * A sex logger for our app
   */
  protected static Logger                      log                = LoggerFactory.getLogger(JavaSitemapGeneratorURL.class);

  /**
   * Googles limit for our extensions
   */
  public static int                            MAX_EXTENSION_SIZE = 1000;

  /**
   * A map that contains everything we always want from a sitemap image url
   */
  protected Map<String, Object>                content            = new LinkedHashMap<String, Object>();

  /**
   * The needed xmlns entries for this url
   */
  protected List<XMLNS>                        xmlns              = new ArrayList<XMLNS>();

  /**
   * Extensions to the normal sitemap
   * e.g. Video, Image, News, Code, Geo and so on
   */
  protected List<JavaSitemapGeneratorURLExtension> urlExtensions      = new ArrayList<JavaSitemapGeneratorURLExtension>(2);

  protected final String                       URL_LOC            = "loc";
  protected final String                       URL_LAST_MOD       = "lastmod";
  protected final String                       URL_CHANGE_FREQ    = "changefreq";
  protected final String                       URL_PRIORITY       = "priority";

  /**
   * Here is only one needed parameter
   * LOC = Location
   * e.g.: http://www.it2media.de
   * 
   * @param loc
   */
  public JavaSitemapGeneratorURL(URL loc) {
    // remember the loc
    content.put(URL_LOC, loc);
    // we need this to export the right xmlns into the sitemap
    addXmlns(XMLNS.SITEMAP09);
  }

  /**
   * Here is only one needed parameter
   * LOC = Location
   * e.g.: http://www.it2media.de
   * 
   * @param loc
   * @throws MalformedURLException
   */
  public JavaSitemapGeneratorURL(String loc) throws MalformedURLException {
    this(new URL(loc));
  }

  /**
   * Returns the location of this url
   * 
   * @return
   */
  public URL getLoc() {
    return (URL) content.get(URL_LOC);
  }

  /**
   * Adds the XMLNS (xml namespace) for the output later
   * 
   * @param xmlns
   * @return
   */
  protected JavaSitemapGeneratorURL addXmlns(XMLNS xmlns) {
    if (!this.xmlns.contains(xmlns))
      this.xmlns.add(xmlns);
    return this;
  }

  public JavaSitemapGeneratorURL addExtension(JavaSitemapGeneratorURLExtension extension) {

    if (urlExtensions.size() < JavaSitemapGeneratorURL.MAX_EXTENSION_SIZE) {
      // add the extension
      urlExtensions.add(extension);
      // add the xmlns
      addXmlns(extension.getXmlns());
      return this;
    }

    throw new ReachedMaxExtensionSizeException(JavaSitemapGeneratorURL.MAX_EXTENSION_SIZE);

  }

  /**
   * Get the date the URL was last modifed, in YYYY-MM-DDThh:mmTZD format (time value is optional).
   * 
   * @return
   */
  public Date getLastMod() {
    return (Date) content.get(URL_LAST_MOD);
  }

  /**
   * Get the date the URL was last modifed, in YYYY-MM-DDThh:mmTZD format (time value is optional).
   * 
   * @return
   */
  public Date getLastModified() {
    return getLastMod();
  }

  /**
   * Set the date the URL was last modifed, in YYYY-MM-DDThh:mmTZD format (time value is optional).
   * 
   * @param lastMod
   * @return
   */
  public JavaSitemapGeneratorURL setLastMod(Date lastMod) {
    content.put(URL_LAST_MOD, lastMod);
    return this;
  }

  /**
   * Set the date the URL was last modifed, in YYYY-MM-DDThh:mmTZD format (time value is optional).
   * 
   * @param lastMod
   * @return
   */
  public JavaSitemapGeneratorURL setLastModified(Date lastMod) {
    return setLastMod(lastMod);
  }

  /**
   * Returns a hint about how frequently the page is likely to change. Valid values are:
   * always - Use for pages that change every time they are accessed.
   * hourly
   * daily
   * weekly
   * monthly
   * yearly
   * never - Use this value for archived URLs.
   * 
   * @return
   */
  public ChangeFreq getChangeFreq() {
    return (ChangeFreq) content.get(URL_CHANGE_FREQ);
  }

  /**
   * Returns a hint about how frequently the page is likely to change. Valid values are:
   * always - Use for pages that change every time they are accessed.
   * hourly
   * daily
   * weekly
   * monthly
   * yearly
   * never - Use this value for archived URLs.
   * 
   * @return
   */
  public ChangeFreq getChangeFrequency() {
    return getChangeFreq();
  }

  /**
   * Sets a hint about how frequently the page is likely to change. Valid values are:
   * always - Use for pages that change every time they are accessed.
   * hourly
   * daily
   * weekly
   * monthly
   * yearly
   * never - Use this value for archived URLs.
   * 
   * @return
   */
  public JavaSitemapGeneratorURL setChangeFreq(ChangeFreq changefreq) {
    content.put(URL_CHANGE_FREQ, changefreq);
    return this;
  }

  /**
   * Sets a hint about how frequently the page is likely to change. Valid values are:
   * always - Use for pages that change every time they are accessed.
   * hourly
   * daily
   * weekly
   * monthly
   * yearly
   * never - Use this value for archived URLs.
   * 
   * @return
   */
  public JavaSitemapGeneratorURL setChangeFrequency(ChangeFreq changefreq) {
    return setChangeFreq(changefreq);
  }

  /**
   * Returns the priority of a URL relative to all the other URLs on the site. This priority can range from 1.0 (extremely important) to 0.1 (not important at all).
   * 
   * Does not affect your site's ranking in Google search results. Because this value is relative to other pages on your site, assigning a high priority (or specifying the same priority for all URLs) will not help your site's search ranking. In addition, setting all pages to the same priority will have no effect.
   * 
   * @return
   */
  public double getPriority() {
    return (Double) content.get(URL_PRIORITY);
  }

  /**
   * Sets the priority of a URL relative to all the other URLs on the site. This priority can range from 1.0 (extremely important) to 0.1 (not important at all).
   * 
   * Does not affect your site's ranking in Google search results. Because this value is relative to other pages on your site, assigning a high priority (or specifying the same priority for all URLs) will not help your site's search ranking. In addition, setting all pages to the same priority will have no effect.
   * 
   * @param priority
   * @throws PriorityOutOfRangeException
   */
  public JavaSitemapGeneratorURL setPriority(double priority) {
    if (priority >= 0.1 && priority <= 1)
      content.put(URL_PRIORITY, priority);
    else
      throw new PriorityOutOfRangeException(priority);
    return this;
  }

  /**
   * Returns the current used date format
   * 
   * @deprecated use global simple date format in StringSitemapGenerator or FileSitemapGenerator, e.g.: StringSitemapGenerator.getSimpleDateFormat() - will be deleted in 1.3
   * @return
   */
  public SimpleDateFormat getSimpleDateFormat() {
    return AbstractJavaSitemapGenerator.getSimpleDateFormat();
  }

  /**
   * Sets a new date format<br>
   * If you like to change this<br>
   * Behold: this is a global date format now!<br>
   * 
   * @param simpleDateFormat
   * @deprecated use global simple date format in StringSitemapGenerator or FileSitemapGenerator, e.g.: StringSitemapGenerator.getSimpleDateFormat() - will be deleted in 1.3
   * @return
   */
  public JavaSitemapGeneratorURL setSimpleDateFormat(SimpleDateFormat simpleDateFormat) {
    // this works because the simple date format is static!
    (new StringJavaSitemapGenerator()).setSimpleDataFormat(simpleDateFormat);
    return this;
  }

  /**
   * Returns the needed xmlns entries for this url
   * 
   * @return
   */
  public List<XMLNS> getXMLNS() {
    return xmlns;
  }

  /**
   * Transfers the url to a String object
   */
  public String toXMLFragment() {

    // we will return the String from the builder
    StringBuilder sb = new StringBuilder("  <url>\n");

    for (Entry<String, Object> entry : content.entrySet()) {
      if (entry.getKey().equals(URL_LAST_MOD)) {
        sb.append("    <" + entry.getKey() + ">" + AbstractJavaSitemapGenerator.getSimpleDateFormat().format(entry.getValue()) + "</" + entry.getKey() + ">\n");
      } else {
        sb.append("    <" + entry.getKey() + ">" + String.valueOf(entry.getValue()) + "</" + entry.getKey() + ">\n");
      }

      // now add additional stuff
      for (JavaSitemapGeneratorURLExtension url : urlExtensions)
        sb.append(url.toXMLFragment());
    }

    // the url part
    sb.append("  </url>\n");

    // return what we want
    return sb.toString();
  }
}
